package com.medusa.aps.business.modules.basic.mp.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.medusa.aps.business.model.constant.RedisConstants;
import com.medusa.aps.business.model.enums.SourceEnum;
import com.medusa.aps.business.model.pagination.Page;
import com.medusa.aps.business.modules.basic.model.input.SupplierDTO;
import com.medusa.aps.business.modules.basic.model.input.SupplierParam;
import com.medusa.aps.business.modules.basic.model.output.SupplierMaterialVO;
import com.medusa.aps.business.modules.basic.model.output.SupplierVO;
import com.medusa.aps.business.modules.basic.mp.entity.Supplier;
import com.medusa.aps.business.modules.basic.mp.entity.SupplierMaterial;
import com.medusa.aps.business.modules.basic.mp.entity.SupplierMaterialDTO;
import com.medusa.aps.business.modules.basic.mp.mapper.SupplierMapper;
import com.medusa.aps.business.modules.basic.mp.service.SupplierMaterialService;
import com.medusa.aps.business.modules.basic.mp.service.SupplierService;
import com.medusa.aps.business.modules.plan.mp.entity.PurchasePlan;
import com.medusa.aps.business.modules.plan.mp.service.PurchasePlanService;
import com.medusa.aps.business.util.PercentageScheduleUtils;
import com.medusa.aps.business.common.model.resp.SystemCode;
import com.medusa.aps.business.common.redis.annotation.Redisson;
import com.medusa.aps.business.global.model.exception.GlobalException;
import com.medusa.aps.business.global.model.exception.collector.DistinctValidator;
import com.medusa.aps.business.global.model.exception.collector.ErrorCollector;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 供应商服务实现类
 *
 * @author yuxh
 * @since 2023-08-14 17:37:50
 */
@Service("supplierService")
public class SupplierServiceImpl extends ServiceImpl<SupplierMapper, Supplier> implements SupplierService {

    @Resource
    private SupplierService supplierService;
    @Resource
    private SupplierMaterialService supplierMaterialService;
    @Resource
    private PurchasePlanService purchasePlanService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Redisson(name = RedisConstants.SUPPLIER_LOCK_KEY, waitTime = 1)
    @Redisson(name = RedisConstants.MATERIAL_LOCK_KEY, waitTime = 1)
    public void saveSupplier(SupplierDTO supplierCreate) {
        validatorSupplier(supplierCreate);
        Long supplierId = IdUtil.getSnowflakeNextId();
        Supplier supplier = new Supplier()
                .setId(supplierId)
                .setSupplierCode(supplierCreate.getSupplierCode())
                .setSupplierName(supplierCreate.getSupplierName())
                .setSupplierPhone(supplierCreate.getSupplierPhone())
                .setSupplierPerson(supplierCreate.getSupplierPerson())
                .setDataSource(SourceEnum.ARTIFICIAL);
        List<SupplierMaterialDTO> materials = supplierCreate.getSupplierMaterials();
        List<SupplierMaterial> supplierMaterials = new ArrayList<>();
        if (CollUtil.isNotEmpty(materials)) {
            for (SupplierMaterialDTO supplierMaterial : materials) {
                supplierMaterials.add(supplierMaterial.newSupplierMaterial(supplierId));
            }
        }
        supplierService.save(supplier);
        if (CollUtil.isNotEmpty(supplierMaterials)) {
            supplierMaterialService.saveBatch(supplierMaterials);
        }

    }

    private void validatorSupplier(SupplierDTO supplierCreate) {
        ErrorCollector errorCollector = DistinctValidator.valid(supplierCreate.getSupplierMaterials(), SupplierMaterialDTO::getMaterialCode, "供应物料编码");
        if (errorCollector.hasError()) {
            throw new GlobalException(errorCollector.toString());
        }
        boolean exists = supplierService.lambdaQuery()
                .eq(Supplier::getSupplierCode, supplierCreate.getSupplierCode())
                .exists();
        if (exists) {
            throw new GlobalException(StrUtil.format("供应商编码:【{}】系统已存在", supplierCreate.getSupplierCode()));
        }

    }

    /**
     * 更新供应商
     *
     * @param supplierDTO 供应商
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Redisson(value = RedisConstants.SUPPLIER_LOCK_KEY, waitTime = 1)
    @Redisson(value = RedisConstants.MATERIAL_LOCK_KEY, waitTime = 1)
    public void updateSupplier(SupplierDTO supplierDTO) {
        // 校验供应商编码
        updateValidSupplier(supplierDTO);
        Supplier supplier = supplierService.getById(supplierDTO.getId());
        if (supplier == null) {
            throw SystemCode.PARAM_VALID_ERROR.msgEx("供应商不存在");
        }
        supplierService.updateById(
                supplier.setSupplierName(supplierDTO.getSupplierName())
                        .setSupplierPhone(supplierDTO.getSupplierPhone())
                        .setSupplierPerson(supplierDTO.getSupplierPerson())
        );
        //物料删除、保存
        List<SupplierMaterial> oldMaterials = supplierMaterialService.lambdaQuery()
                .eq(SupplierMaterial::getSupplierId, supplier.getId())
                .list();
        List<SupplierMaterialDTO> supplierMaterials = supplierDTO.getSupplierMaterials();
        supplierMaterials = CollUtil.isEmpty(supplierMaterials) ? List.of() : supplierMaterials;
        List<SupplierMaterial> newMaterials = supplierMaterials.stream()
                .map(supplierMaterialDTO -> supplierMaterialDTO.newSupplierMaterial(supplierDTO.getId()))
                .toList();
        //校验需要删除的物料是否有采购订单
        Set<String> oldMaterialCode = oldMaterials.stream().map(SupplierMaterial::getMaterialCode).collect(Collectors.toSet());
        Set<String> newMaterialCode = newMaterials.stream().map(SupplierMaterial::getMaterialCode).collect(Collectors.toSet());
        Collection<String> subtract = CollUtil.subtract(oldMaterialCode, newMaterialCode);
        if (CollUtil.isNotEmpty(subtract)) {
            PercentageScheduleUtils.valid(
                    purchasePlanService.lambdaQuery()
                            .select(PurchasePlan::getItemCode)
                            .in(PurchasePlan::getItemCode, subtract)
                            .eq(PurchasePlan::getSupplierId, supplierDTO.getId())
                            .list(),
                    PurchasePlan::getItemCode,
                    "供应商下的物料编码:【{}】存在采购计划,不能删除"
            );
        }
        // 删除旧的物料
        if (CollUtil.isNotEmpty(oldMaterials)) {
            supplierMaterialService.lambdaUpdate()
                    .eq(SupplierMaterial::getSupplierId, supplierDTO.getId())
                    .remove();
        }
        // 保存新的物料
        if (CollUtil.isNotEmpty(newMaterials)) {
            supplierMaterialService.saveBatch(
                    newMaterials
            );
        }

    }

    private void updateValidSupplier(SupplierDTO supplierDTO) {
        ErrorCollector errorCollector = DistinctValidator.valid(supplierDTO.getSupplierMaterials(), SupplierMaterialDTO::getMaterialCode, "供应物料编码");
        if (errorCollector.hasError()) {
            throw new GlobalException(errorCollector.toString());
        }
    }

    /**
     * 分页查询所有数据
     *
     * @param supplierParam 分页参数
     * @return 所有数据
     */
    @Override
    public Page<Supplier> pageSupplier(SupplierParam supplierParam) {
        return baseMapper.pageSupplier(supplierParam);
    }

    /**
     * 查询供应商详情
     *
     * @param id 供应商id
     * @return 供应商详情
     */
    @Override
    public SupplierVO getSupplierDetail(Long id) {
        Supplier supplier = supplierService.lambdaQuery()
                .eq(Supplier::getId, id)
                .one();
        SupplierVO supplierVO = new SupplierVO();
        if (supplier == null) {
            return supplierVO;
        }
        supplierVO.setId(supplier.getId())
                .setSupplierCode(supplier.getSupplierCode())
                .setSupplierName(supplier.getSupplierName())
                .setSupplierPhone(supplier.getSupplierPhone())
                .setSupplierPerson(supplier.getSupplierPerson());
        List<SupplierMaterial> supplierMaterials = supplierMaterialService.lambdaQuery()
                .eq(SupplierMaterial::getSupplierId, id)
                .list();
        if (CollectionUtil.isEmpty(supplierMaterials)) {
            return supplierVO;
        }
        supplierVO.setSupplierMaterials(supplierMaterials.stream()
                .map(supplierMaterial ->
                        new SupplierMaterialVO()
                                .setSupplierId(supplierMaterial.getSupplierId())
                                .setMaterialId(supplierMaterial.getMaterialId())
                                .setMaterialCode(supplierMaterial.getMaterialCode())
                                .setMaterialName(supplierMaterial.getMaterialName())
                                .setFinishedProductRate(supplierMaterial.getFinishedProductRate())
                                .setQuasiDeliveryRate(supplierMaterial.getQuasiDeliveryRate())
                ).toList()
        );
        return supplierVO;
    }


    /**
     * 删除数据供应商物料
     *
     * @param supplierIds 供应商id集合
     * @param materialId  物料id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Redisson(name = RedisConstants.SUPPLIER_LOCK_KEY, waitTime = 1)
    public void deleteSupplier(List<Long> supplierIds, Long materialId) {
        boolean notEmpty = CollUtil.isNotEmpty(supplierIds);
        LocalDate currentDate = LocalDate.now();
        if (notEmpty && materialId != null) {
            SupplierMaterial supplierMaterial = supplierMaterialService.lambdaQuery()
                    .select(SupplierMaterial::getId, SupplierMaterial::getMaterialId,
                            SupplierMaterial::getMaterialName, SupplierMaterial::getMaterialCode)
                    .eq(SupplierMaterial::getMaterialId, materialId)
                    .eq(SupplierMaterial::getSupplierId, supplierIds.get(0))
                    .one();
            boolean exists = purchasePlanService.lambdaQuery()
                    .eq(PurchasePlan::getMaterialId, supplierMaterial.getMaterialId())
                    .eq(PurchasePlan::getSupplierId, supplierMaterial.getSupplierId())
                    .gt(PurchasePlan::getProcurementLeadTime, currentDate)
                    .exists();
            if (exists) {
                throw new GlobalException(StrUtil.format("供应商下的物料编码:【{}】存在采购计划,不能删除", supplierMaterial.getMaterialCode()));
            }
            supplierMaterialService.lambdaUpdate()
                    .eq(SupplierMaterial::getMaterialId, materialId)
                    .eq(SupplierMaterial::getSupplierId, supplierIds.get(0))
                    .remove();
            return;
        }
        if (notEmpty) {
            PercentageScheduleUtils.valid(
                    purchasePlanService.lambdaQuery()
                            .select(PurchasePlan::getSupplierName)
                            .in(PurchasePlan::getSupplierId, supplierIds)
                            .gt(PurchasePlan::getProcurementLeadTime, currentDate)
                            .list(),
                    PurchasePlan::getSupplierName,
                    "供应商:【{}】下存在采购计划,不能删除"
            );
            supplierService.lambdaUpdate()
                    .in(Supplier::getId, supplierIds)
                    .remove();
            supplierMaterialService.lambdaUpdate()
                    .in(SupplierMaterial::getSupplierId, supplierIds)
                    .remove();
        }
    }
}

